package com.himongo.util;

import java.util.ArrayList;
import java.util.List;

import org.bson.Document;
import org.bson.conversions.Bson;

import com.alibaba.fastjson.JSONObject;
import com.hibson.util.DocumentUtil;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;

/**
 * 文档数据库中集合操作辅助类.
 *
 * @author xuminrong
 *
 */
public final class CollectionUtil {
	/**
	 * 默认IP.
	 */
	private static final String DEFAULT_IP = "127.0.0.1";

	/**
	 * 默认端口.
	 */
	private static final int DEFAULT_PORT = 27017;

	/**
	 * 取得文档数据库IP.
	 * @return IP.
	 */
	public String getMongoIP() {
		return mongoIP;
	}

	/**
	 * 设置文档数据库IP.
	 * @param ip 文档数据库IP.
	 */
	public void setMongoIP(final String ip) {
		this.mongoIP = ip;
	}

	/**
	 * 获取文档数据库端口.
	 * @return 端口.
	 */
	public int getMongoPort() {
		return mongoPort;
	}

	/**
	 * 设置文档数据库端口.
	 * @param port 文档数据库端口
	 */
	public void setMongoPort(final int port) {
		this.mongoPort = port;
	}

	/**
	 * 取得文档数据库集合名称.
	 * @return 集合名称.
	 */
	public String getCollectName() {
		return collectName;
	}

	/**
	 * 设置文档数据库集合.
	 * @param name 集合名称.
	 */
	public void setCollectName(final String name) {
		this.collectName = name;
	}

	/**
	 * 获取文档数据库.
	 * @return 数据库名称.
	 */
	public String getDbName() {
		return dbName;
	}

	/**
	 * 设置文档数据库.
	 * @param name 数据库名称
	 */
	public void setDbName(final String name) {
		this.dbName = name;
	}

	/**
	 * 取得文档数据库中的所有集合.
	 * @return 数据库下所有集合.
	 */
	public MongoCollection<Document> getCollection() {
		if (null == collection) {
			if (null == mongoClient) {
				mongoClient = new MongoClient(mongoIP, mongoPort);
			}
			MongoDatabase db = mongoClient.getDatabase(dbName);
			collection = db.getCollection(collectName);
			if (null == collection) {
				db.createCollection(collectName);
				collection = db.getCollection(collectName);
			}
		}
		return collection;
	}

	/**
	 * 是否存在查询.
	 *
	 * @param filter 查询条件.
	 * @return 存在/不存在
	 */
	public boolean isExist(final Bson filter) {
		FindIterable<Document> result = getCollection().find(filter).skip(0).limit(1)
				.projection(Projections.excludeId());
		if (null == result) {
			return false;
		}

		return result.first() != null;
	}

	/**
	 * 符合查询条件的子节点元素是否存在.
	 * @param filter 记录查询条件.
	 * @param fields 查询属性.
	 * @param sliceFilter 数组条件.
	 * @return 节点是否存在.
	 */
	public boolean isChildSetElementExist(final Bson filter, final String fields, final Bson sliceFilter) {
		Bson newFilter = Filters.and(filter, sliceFilter);
		Bson include = Projections.include(DocumentUtil.getFirstField(fields), fields);
		Bson projection = Projections.fields(include, Projections.excludeId());
		FindIterable<Document> result = getCollection().find(newFilter).limit(1).projection(projection);
		if (null == result) {
			return false;
		}

		Document doc = result.first();
		if (null == doc) {
			return false;
		}
		return true;
	}

	/**
	 * 向数组节点中添加数据.
	 * @param filter 记录查询条件.
	 * @param fields 节点属性.
	 * @param doc 要添加的数据.
	 * @return 成功/失败.
	 */
	public boolean addItem2set(final Bson filter, final String fields, final Document doc) {
		Bson update = Updates.addToSet(fields, doc);
		UpdateResult result = getCollection().updateOne(filter, update);
		return result.isModifiedCountAvailable();
	}

	/**
	 * 从数组节点中删除数据.
	 * @param filter 记录查询条件.
	 * @param fields 查询属性.
	 * @param doc 要删除的数据.
	 * @return 成功/失败.
	 */
	public boolean dropItemFromSet(final Bson filter, final String fields, final Document doc) {
		Bson update = Updates.pull(fields, doc);
		UpdateResult result = getCollection().updateOne(filter, update);
		return result.isModifiedCountAvailable();
	}

	/**
	 * 请求数组节点数据.
	 * @param filter 记录查询条件.
	 * @param sliceFilter 节点数组查询条件.
	 * @param slicFields 数组节点的属性.
	 * @param arrFields 数组节点的属性.
	 * @return 查询结果.
	 */
	public Document queryItemFromSet(final Bson filter, final Bson sliceFilter, final String slicFields,
			final String arrFields) {
		Bson newFilter = Filters.and(filter, sliceFilter);
		return queryItemFromSet(newFilter, slicFields, arrFields);
	}

	/**
	 * 请求数组节点下面的集合（分页）.
	 * @param filter 记录查询条件。
	 * @param arrFields 集合属性。
	 * @param start 开始位置。
	 * @param length 查询数量。
	 * @return 查询结果。
	 */
	public List<Document> queryItemsFromSet(final Bson filter, final String arrFields, final int start,
			final int length) {
		Bson include = Projections.include(DocumentUtil.getFirstField(arrFields), arrFields);
		Bson slice = Projections.slice(arrFields, start, length);
		Bson projection = Projections.fields(include, Projections.excludeId(), slice);
		FindIterable<Document> result = getCollection().find(filter).limit(1).projection(projection);
		if (null == result) {
			return null;
		}
		Document doc = result.first();
		if (null == doc) {
			return null;
		}
		return DocumentUtil.getArrayByFeild(doc, arrFields);
	}

	/**
	 * 从数组节点中查询数据.
	 * @param filter 记录查询条件。
	 * @param arrFields 数组节点。
	 * @return 查询结果。
	 */
	public List<Document> queryItemsFromSet(final Bson filter, final String arrFields) {
		Bson include = Projections.include(DocumentUtil.getFirstField(arrFields), arrFields);
		Bson projection = Projections.fields(include, Projections.excludeId());
		FindIterable<Document> result = getCollection().find(filter).limit(1).projection(projection);
		if (null == result) {
			return null;
		}
		Document doc = result.first();
		if (null == doc) {
			return null;
		}
		return DocumentUtil.getArrayByFeild(doc, arrFields);
	}

	/**
	 * 从数组节点中查询一条数据.
	 * @param filter 记录查询条件.
	 * @param elematchFields 属性.
	 * @param arrFields 数组属性.
	 * @return 查询结果.
	 */
	public Document queryItemFromSet(final Bson filter, final String elematchFields, final String arrFields) {
		Bson include = Projections.include(DocumentUtil.getFirstField(arrFields), arrFields);
		Bson projection;
		if (elematchFields != null && !elematchFields.trim().equals("")) {
			Bson elemMatch = Projections.elemMatch(elematchFields);
			projection = Projections.fields(include, Projections.excludeId(), elemMatch);
		} else {
			projection = Projections.fields(include, Projections.excludeId());
		}
		FindIterable<Document> result = getCollection().find(filter).limit(1).projection(projection);
		if (null == result) {
			return null;
		}
		Document doc = result.first();
		if (null == doc) {
			return null;
		}
		List<Document> docs = DocumentUtil.getArrayByFeild(doc, arrFields);
		if (null == docs) {
			return null;
		}

		if (docs.size() < 1) {
			return null;
		}
		return docs.get(0);
	}

	/**
	 * 节点是否存在.
	 * @param filter 记录查询条件.
	 * @param fields 节点属性.
	 * @return 是否存在.
	 */
	public boolean isNodeExist(final Bson filter, final String fields) {
		Bson newFilter = Filters.and(filter, Filters.exists(fields));
		return isExist(newFilter);
	}

	/**
	 * 执行删除操作.
	 *
	 * @param filter 记录查询条件.
	 * @return 删除成功/失败.
	 */
	public boolean deleteOne(final Bson filter) {
		DeleteResult result = getCollection().deleteOne(filter);
		return result.getDeletedCount() > 0;
	}

	/**
	 * 执行更新操作.
	 *
	 * @param filter 记录查询条件.
	 * @param update 更新后的数据.
	 * @return 成功/失败.
	 */
	public boolean updateOne(final Bson filter, final Bson update) {
		UpdateResult result = getCollection().updateOne(filter, update);
		return result.isModifiedCountAvailable();
	}

	/**
	 * 查找一条记录.
	 * @param filter 记录查询条件.
	 * @param projection 要返回的数据.
	 * @return 返回结果.
	 */
	public Document filterOne(final Bson filter, final Bson projection) {
		FindIterable<Document> result = getCollection().find(filter).skip(0).limit(1).projection(projection);
		if (null == result) {
			return null;
		}

		return result.first();
	}

	/**
	 * 将查询结果转化为Document集合.
	 *
	 * @param result 查询结果.
	 * @return 转换后的对象集合.
	 */
	public static List<Document> getList(final FindIterable<Document> result) {
		List<Document> list = new ArrayList<Document>();
		if (null == result) {
			return list;
		}
		MongoCursor<Document> cursor = result.iterator();
		while (cursor.hasNext()) {
			Document doc = cursor.next();
			if (null == doc) {
				continue;
			}

			list.add(doc);
		}
		return list;
	}

	/**
	 * 将查询结果转化为List.
	 *
	 * @param <T> 转换后的数据类型.
	 * @param result 查询结果.
	 * @param classz 转换类型.
	 * @return 转换后的对象集合.
	 */
	public static <T> List<T> getList(final FindIterable<Document> result, final Class<T> classz) {
		List<T> list = new ArrayList<T>();
		if (null == result) {
			return list;
		}
		MongoCursor<Document> cursor = result.iterator();
		while (cursor.hasNext()) {
			Document doc = cursor.next();
			if (null == doc) {
				continue;
			}

			String json = doc.toJson();
			if (json != null && json != "") {
				T t = JSONObject.parseObject(json, classz);
				if (t != null) {
					list.add(t);
				}
			}
		}
		return list;
	}

	/**
	 * 数据库IP.
	 */
	private String mongoIP = DEFAULT_IP;

	/**
	 * 数据库端口.
	 */
	private int mongoPort = DEFAULT_PORT;

	/**
	 * 集合名称.
	 */
	private String collectName = null;

	/**
	 * 数据库名称.
	 */
	private String dbName = null;

	/**
	 * 客户端.
	 */
	private MongoClient mongoClient = null;

	/**
	 * 集合.
	 */
	private MongoCollection<Document> collection = null;
}
